﻿//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Common Core Extensions
//===============================================================================

using System;
using System.IO;
using System.Reflection;
using System.Globalization;
using System.Windows.Forms;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;
using EntLibContrib.Common.Properties;

namespace EntLibContrib.Common.Configuration.Design.Editors
{
	/// <summary>
	/// Represents the UI for the assembly selector dialog.
	/// </summary>
	public partial class MethodSelectorUI : Form
	{
		#region Fields
		private OpenFileDialog openFileDialog = new OpenFileDialog();
		private MethodSelector selector;
		private BaseTypeAttribute baseTypeAttribute;
		#endregion

		#region Properties
		/// <summary>
		/// Gets the <see cref="TypeMethodName"/> selected for use.
		/// </summary>
		/// <value>The <see cref="TypeMethodName"/> selected for use.</value>
		public TypeMethodName SelectedMethod
		{
			get { return this.treeView.SelectedNode.Tag as TypeMethodName; }
		}

		/// <summary>
		/// Gets the <see cref="ImageList"/> for the types.
		/// </summary>
		/// <value>The <see cref="ImageList"/> for the types.</value>
		public ImageList MethodImageList
		{
			get { return this.methodImageList; }
		}
		#endregion

		#region Construction
		/// <summary>
		/// <para>Initialize a new instance of the <see cref="MethodSelectorUI"/> class.</para>
		/// </summary>
		public MethodSelectorUI()
		{
			InitializeComponent();
			openFileDialog.Filter = InternalResources.AssemblyFileFilter;
		}

		/// <summary>
		/// Initialize a new instance of the <see cref="MethodSelectorUI"/> class.
		/// </summary>
		/// <param name="currentTypeMethodName">Name of the current service method.</param>
		/// <param name="baseTypeAttribute">The base type attribute.</param>
		/// <param name="methodFilterAttribute">The method filter attribute.</param>
		public MethodSelectorUI(TypeMethodName currentTypeMethodName, BaseTypeAttribute baseTypeAttribute, MethodFilterAttribute methodFilterAttribute)
			: this()
		{
			this.baseTypeAttribute = baseTypeAttribute;
			this.lblFilter.Text = BuildFilterLabel();
			this.openFileDialog.DereferenceLinks = false;
			this.selector = new MethodSelector(currentTypeMethodName, baseTypeAttribute, methodFilterAttribute, this.treeView);
		}
		#endregion

		#region Event Handlers
		/// <summary>
		/// Called when the browse button is clicked.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
		private void OnBrowseButtonClick(object sender, EventArgs e)
		{
			LoadAssembly(() =>
				{
					DialogResult result = this.openFileDialog.ShowDialog(this);
					if (result == DialogResult.OK)
					{
						string originalAssemblyFileName = this.openFileDialog.FileName;

						using (AssemblyLoader loaderHook = new AssemblyLoader(originalAssemblyFileName, Path.GetDirectoryName(originalAssemblyFileName)))
						{
							return Assembly.LoadFrom(loaderHook.CopiedAssemblyPath);
						}
					}
					else
						return null;
				});
		}

		/// <summary>
		/// Called when a node on the tree view is double-clicked.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnTreeViewDoubleClick(object sender, EventArgs e)
		{
			this.OnOkButtonClick(this, EventArgs.Empty);
		}

		/// <summary>
		/// Called when the OK button is clicked.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnOkButtonClick(object sender, EventArgs e)
		{
			if (SelectedMethod == null)
				DisplayMessageBox(InternalResources.ExceptionNoMethodSelectedMessage, InternalResources.ExceptionNoMethodSelectedCaption, MessageBoxIcon.Exclamation);
			else
			{
				DialogResult = DialogResult.OK;
				Close();
			}
		}

		/// <summary>
		/// Called when the cancel button is clicked.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnCancelButtonClick(object sender, EventArgs e)
		{
			DialogResult = DialogResult.Cancel;
			Close();
		}

		/// <summary>
		/// Called when the Expand button is clicked.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnExpandButtonClick(object sender, EventArgs e)
		{
			this.selector.ExpandAssemblyNodes();
		}

		/// <summary>
		/// Called when the Collapse button is clicked.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnCollapseButtonClick(object sender, EventArgs e)
		{
			this.selector.CollapseAssemblyNodes();
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Loads an assembly into the tree view.
		/// </summary>
		/// <param name="loadAssemblyFromFile">The load assembly from file function.</param>
		private void LoadAssembly(Func<Assembly> loadAssemblyFromFile)
		{
			Assembly assembly = null;

			try
			{
				if ((assembly = loadAssemblyFromFile()) != null)
				{
					try
					{
						this.treeView.BeginUpdate();

						TreeNode assemblyNode = this.selector.LoadTreeView(assembly);
						if (assemblyNode == null)
							DisplayMessageBox(string.Format(CultureInfo.CurrentCulture, InternalResources.NoTypesFoundInAssemblyErrorMessage, assembly.GetName().Name, this.baseTypeAttribute.BaseType.FullName), InternalResources.NoTypesFoundInAssemblyCaption);
						else
							this.selector.PositionTreeView(assemblyNode);
					}
					finally
					{
						this.treeView.EndUpdate();
					}
				}
			}
			catch (FileNotFoundException ex)
			{
				DisplayMessageBox(string.Format(CultureInfo.CurrentCulture, InternalResources.AssemblyLoadFailedErrorMessage, ex.Message), string.Empty);
				return;
			}
			catch (BadImageFormatException ex)
			{
				DisplayMessageBox(string.Format(CultureInfo.CurrentCulture, InternalResources.AssemblyLoadFailedErrorMessage, ex.Message), string.Empty);
				return;
			}
			catch (ReflectionTypeLoadException ex)
			{
				DisplayMessageBox(string.Format(CultureInfo.CurrentCulture, InternalResources.EnumTypesFailedErrorMessage, ex.Message), string.Empty);
				return;
			}
		}

		/// <summary>
		/// Displays the message box.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="caption">The caption.</param>
		private void DisplayMessageBox(string message, string caption)
		{
			DisplayMessageBox(message, caption, MessageBoxIcon.Information);
		}

		/// <summary>
		/// Displays the message box.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="caption">The caption.</param>
		/// <param name="icon">The icon.</param>
		private void DisplayMessageBox(string message, string caption, MessageBoxIcon icon)
		{
			MessageBoxOptions options = (MessageBoxOptions)0;

			if (RightToLeft == RightToLeft.Yes)
				options = MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign;

			MessageBox.Show(message, caption, MessageBoxButtons.OK, icon, MessageBoxDefaultButton.Button1, options);
		}

		/// <summary>
		/// Builds the form UI label that describes the filter conditions.
		/// </summary>
		/// <returns>filter label text</returns>
		private string BuildFilterLabel()
		{
			string filterLabel = string.Empty;

			if (baseTypeAttribute.BaseType.IsInterface)
				filterLabel = FormatFilterLabel(InternalResources.MethodSelectorInterfaceRootNodeText, InternalResources.MethodSelectorInterfaceRootNodeTextWithConfigurationType);
			else if (baseTypeAttribute.BaseType.IsClass)
				filterLabel = FormatFilterLabel(InternalResources.MethodSelectorClassRootNodeText, InternalResources.MethodSelectorClassRootNodeTextWithConfigurationType);

			return filterLabel;
		}

		/// <summary>
		/// Formats the filter label.
		/// </summary>
		/// <param name="labelText">The label text.</param>
		/// <param name="labelTextWithConfigurationType">The label text with configuration type.</param>
		/// <returns>formatted label</returns>
		private string FormatFilterLabel(string labelText, string labelTextWithConfigurationType)
		{
			string formattedLabel = string.Empty;

			if (baseTypeAttribute.ConfigurationType == null)
				formattedLabel = string.Format(CultureInfo.CurrentUICulture, labelText, baseTypeAttribute.BaseType.FullName);
			else
				formattedLabel = string.Format(CultureInfo.CurrentUICulture, labelTextWithConfigurationType, baseTypeAttribute.BaseType.FullName, baseTypeAttribute.ConfigurationType.FullName);

			return formattedLabel;
		}
		#endregion
	}
}
